#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <cmath>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h> /* for MAXPATHLEN */
#include <sys/time.h>
#include <utime.h>

using namespace std;

//We use msg in chars to pass the info.
//use c++ for using log
#define TOKENMAX 200
#define SEQLENGTH 8		//sequence number length in bytes
#define IDLENGTH 1		//Network id length in bytes
#define SIZELENGTH 2
#define TSIZE 16
#define FSIZE 1024		//Frame size in bytes
#define PORT1 7653		//Port number of the reciever
#define PORT2 7654		//Port number of the reciever
#define PKGSIZE 30
#define MINPSIZE 26
#define MAXPSIZE 99		//since we use two number to represent the size of packet
#define MAXBURST 10

int debugmode = 0;

long timestamp() {
	time_t ltime;
	struct timeval ts;
	long timeinms;
	ltime = time(NULL);
	gettimeofday(&ts, NULL);
	timeinms = ts.tv_sec * 1000000 + ts.tv_usec;
	//	printf("Here is the time in timeinms: %ld\n", timeinms);
	return timeinms;
}

//random generatea timestamp in 2 to 6 seconds.
long newburstflag(long range) {
	long t;
	t = timestamp();
	t += range;
	return t;
}

//return randomsize packet within range
int giverandom(int min, int max){
	int range = max-min;
	srand ( time(NULL) );
	return rand()%range+min;
}

//our timestamp take 16 digits
void buildframe(int cid, char *buf, int seq, int fsize) {
	int junknum = fsize - IDLENGTH - SEQLENGTH - TSIZE;
	int i;
	if (junknum < 0) {
		printf("Error of frame making\n");
		exit(EXIT_FAILURE);
	}

	snprintf(buf, 26, "%01d%02d%08d%016ld", cid, fsize, seq, timestamp());
	buf = buf + IDLENGTH + SIZELENGTH + SEQLENGTH + TSIZE;
	for (i = 0; i < junknum; i++) {
		sprintf(buf, "%01d", 0);
		buf++;
	}
	//sprintf(buf, "%ld", timestamp());
	//	printf("%s\n",buf);
}

int getsizefrompacket(char* buffer) {
	char temp[SIZELENGTH];
	int size;
	strncpy(temp, buffer + IDLENGTH, SIZELENGTH);
	size = atoi(temp);
	while(size>(SIZELENGTH*10))
		size=size/10;
	return size;
}


void delay(int rate) {
	float rateF, delaynsec;
	int delaysec;
	struct timespec tmReq = { 0 }, tmRem = { 0 };
	rateF = (float) rate;
	delaysec = (int) 1 / rateF;
	tmReq.tv_sec = delaysec;
	delaynsec = (float) 1 / rate - delaysec;
	tmReq.tv_nsec = delaynsec * 1000000000L;
	//	printf("in delay1 time %f \n", delaynsec);
	nanosleep(&tmReq, &tmRem);
}

void delay2(int rate) {
	float rateF, delaynsec, time;
	int delaysec;
	struct timespec tmReq = { 0 }, tmRem = { 0 };
	float ran;
	while ((ran = (float) rand() / (float) RAND_MAX) == 0)
		;
	rateF = (float) rate;
	time = (logf(ran) / rateF) * -1;
	delaysec = (int) time;
	tmReq.tv_sec = delaysec;
	delaynsec = time - delaysec;
	tmReq.tv_nsec = delaynsec * 1000000000L;
	//	printf("in delay2 time %f \n", time);
	nanosleep(&tmReq, &tmRem);
}
int main(int argc, char *argv[]) {
	int cid, rate, delaymode, psize, sock, seqnum, bytes_sent, buffer_length, sent,
			burstmode, setbrate,burstrate,port;
	long test, burstflag;
	struct sockaddr_in sa;
	char buffer[FSIZE];


	psize = PKGSIZE;
	if (argc != 6) {
		printf("argc: %d\n", argc);
		printf(
				"Format: rout id rate delaymode burstrate (id: 1 or 2 ; rate:sending rate in packets per second;"
					"delaymode: 0: sending delay = 1/R ; 1:sending delay = -log(rand)/R, where rand is random number 0~1 ; 2: burstmode"
					" burst rate: default 5, if burstrate ==0, means random"
					" randomFlag: 0 for random sized packet, other for fixed sized packet with n bytes");
		exit(EXIT_FAILURE);
	}

	cid = atoi(argv[1]);
	if(cid == 1)
		port = PORT1;
	else
		port = PORT2;
	rate = atoi(argv[2]);
	delaymode = atoi(argv[3]);
	setbrate = atoi(argv[4]);
	psize = atoi(argv[5]);

	burstmode = 1;
	burstflag = 0;

	sent = 0;
	seqnum = 0;

	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (-1 == sock) /* if socket failed to initialize, exit */
	{
		printf("Error Creating Socket");
		exit(EXIT_FAILURE);
	}

	memset(&sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	sa.sin_addr.s_addr = INADDR_ANY;
	sa.sin_port = htons(port);

		for (int p = 0; p < 100; p++) {
//	while (1) {
		if (delaymode == 1)
			delay2(rate);
		else if (delaymode == 0)
			delay(rate);
		else {
			if (burstmode == 0) {
				delay(rate);
				test = timestamp();
				if (test > burstflag) {
					if (debugmode)
						printf("ct: %ld, rt: %ld change to fast\n", test,
								burstflag);
					burstmode = 1;
					burstflag = newburstflag(2500000);
				}
			} else {
				if(setbrate)
					burstrate = setbrate;
				else
					burstrate = giverandom(1, MAXBURST);
				delay(rate * burstrate);
				test = timestamp();
				if (test > burstflag) {
					if (debugmode)
						printf("ct: %ld, rt: %ld change to turbo\n", test,
								burstflag);
					burstmode = 0;
					burstflag = newburstflag(8000000);
				}
			}
		}

		if(psize)
			buildframe(cid, buffer, seqnum, psize);
		else
			buildframe(cid, buffer, seqnum, giverandom(MINPSIZE, MAXPSIZE));
		if (debugmode)
			printf("here is the sending frame %s, its size is %d\n", buffer,
					getsizefrompacket(buffer));
		seqnum++;
		buffer_length = FSIZE;
		bytes_sent = sendto(sock, buffer, buffer_length, 0,
				(struct sockaddr*) &sa, sizeof(struct sockaddr_in));
		if (bytes_sent < 0)
			printf("Error sending packet: %s\n", strerror(errno));
		sent++;
	}
	//close(sock); /* close the socket */	//comment out for c++
	return 0;
}
